/*
============================================================================
Name        : dd_printf.c
Author      : Jian(raphyer@hotmail.com)
Version     :
Copyright   : GPL
Description : dd_printf.c - source file
============================================================================
*/

// INCLUDE FILES
#include "dd_define.h"
#include "dd_printf.h"
#include "pd_unicode.h"


int	dd_snprintf(char* buffer, size_t bufsize, const char* format, ...)
{
    va_list	ap ;	
    int bytes = 0 ;
    va_start(ap, format);
    bytes = dd_vsnprintf(buffer, bufsize, format, ap);
    va_end(ap);
    return (bytes);
}

int                                 /* O - Number of bytes formatted */
dd_vsnprintf(char       *buffer,    /* O - Output buffer */
             size_t     bufsize,    /* O - Size of output buffer */
             const char *format,    /* I - Printf-style format string */
             va_list    ap)         /* I - Pointer to additional arguments */
{
    char *bufptr,		/* Pointer to position in buffer */
         *bufend,		/* Pointer to end of buffer */
         sign,			/* Sign of format width */
         size,			/* Size character (h, l, L) */
         type;			/* Format type character */
    int  width,			/* Width of field */
         prec;          /* Number of characters of precision */
    char tformat[100],  /* Temporary format string for sprintf() */
        *tptr,          /* Pointer into temporary format */
        temp[1024];     /* Buffer for formatted numbers */
    char		*s;     /* Pointer to string */
    int		slen;       /* Length of string */
    int		bytes;      /* Total number of bytes needed */


    /*
    * Loop through the format string, formatting as needed...
    */

    bufptr = buffer;
    bufend = buffer + bufsize - 1;
    bytes  = 0;

    while (*format)
    {
        if (*format == '%')
        {
            tptr = tformat;
            *tptr++ = *format++;

            if (*format == '%')
            {
                if (bufptr && bufptr < bufend) *bufptr++ = *format;
                bytes ++;
                format ++;
                continue;
            }
            else if (strchr(" -+#\'", *format))
            {
                *tptr++ = *format;
                sign = *format++;
            }
            else
                sign = 0;

            if (*format == '*')
            {
                /*
                * Get width from argument...
                */

                format ++;
                width = va_arg(ap, int);

                dd_snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width);
                tptr += strlen(tptr);
            }
            else
            {
                width = 0;

                while (isdigit(*format & 255))
                {
                    if (tptr < (tformat + sizeof(tformat) - 1))
                        *tptr++ = *format;

                    width = width * 10 + *format++ - '0';
                }
            }

            if (*format == '.')
            {
                if (tptr < (tformat + sizeof(tformat) - 1))
                    *tptr++ = *format;

                format ++;

                if (*format == '*')
                {
                    /*
                    * Get precision from argument...
                    */

                    format ++;
                    prec = va_arg(ap, int);

                    dd_snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec);
                    tptr += strlen(tptr);
                }
                else
                {
                    prec = 0;

                    while (isdigit(*format & 255))
                    {
                        if (tptr < (tformat + sizeof(tformat) - 1))
                            *tptr++ = *format;

                        prec = prec * 10 + *format++ - '0';
                    }
                }
            }
            else
                prec = -1;

            if (*format == 'l' && format[1] == 'l')
            {
                size = 'L';

                if (tptr < (tformat + sizeof(tformat) - 2))
                {
                    *tptr++ = 'l';
                    *tptr++ = 'l';
                }

                format += 2;
            }
            else if (*format == 'h' || *format == 'l' || *format == 'L')
            {
                if (tptr < (tformat + sizeof(tformat) - 1))
                    *tptr++ = *format;

                size = *format++;
            }

            if (!*format)
                break;

            if (tptr < (tformat + sizeof(tformat) - 1))
                *tptr++ = *format;

            type  = *format++;
            *tptr = '\0';

            switch (type)
            {
            case 'E' : /* Floating point formats */
            case 'G' :
            case 'e' :
            case 'f' :
            case 'g' :
                if ((width + 2) > sizeof(temp))
                    break;

                sprintf(temp, tformat, va_arg(ap, double));

                bytes += strlen(temp);

                if (bufptr)
                {
                    if ((bufptr + strlen(temp)) > bufend)
                    {
                        strncpy(bufptr, temp, (size_t)(bufend - bufptr));
                        bufptr = bufend;
                    }
                    else
                    {
                        strcpy(bufptr, temp);
                        bufptr += strlen(temp);
                    }
                }
                break;

            case 'B' : /* Integer formats */
            case 'X' :
            case 'b' :
            case 'd' :
            case 'i' :
            case 'o' :
            case 'u' :
            case 'x' :
                if ((width + 2) > sizeof(temp))
                    break;
                sprintf(temp, tformat, va_arg(ap, int));

                bytes += strlen(temp);

                if (bufptr)
                {
                    if ((bufptr + strlen(temp)) > bufend)
                    {
                        strncpy(bufptr, temp, (size_t)(bufend - bufptr));
                        bufptr = bufend;
                    }
                    else
                    {
                        strcpy(bufptr, temp);
                        bufptr += strlen(temp);
                    }
                }
                break;

            case 'p' : /* Pointer value */
                if ((width + 2) > sizeof(temp))
                    break;

                sprintf(temp, tformat, va_arg(ap, void *));

                bytes += strlen(temp);

                if (bufptr)
                {
                    if ((bufptr + strlen(temp)) > bufend)
                    {
                        strncpy(bufptr, temp, (size_t)(bufend - bufptr));
                        bufptr = bufend;
                    }
                    else
                    {
                        strcpy(bufptr, temp);
                        bufptr += strlen(temp);
                    }
                }
                break;

            case 'c' : /* Character or character array */
                bytes += width;

                if (bufptr)
                {
                    if (width <= 1)
                        *bufptr++ = va_arg(ap, int);
                    else
                    {
                        if ((bufptr + width) > bufend)
                            width = bufend - bufptr;

                        memcpy(bufptr, va_arg(ap, char *), (size_t)width);
                        bufptr += width;
                    }
                }
                break;

            case 's' : /* String */
                if ((s = va_arg(ap, char *)) == NULL)
                    s = "(null)";
                
                slen = strlen(s);
                if (slen > width && prec != width)
                    width = slen;

                bytes += width;

                if (bufptr)
                {
                    if ((bufptr + width) > bufend)
                        width = bufend - bufptr;

                    if (slen > width)
                        slen = width;

                    if (sign == '-')
                    {
                        strncpy(bufptr, s, (size_t)slen);
                        memset(bufptr + slen, ' ', (size_t)(width - slen));
                    }
                    else
                    {
                        memset(bufptr, ' ', (size_t)(width - slen));
                        strncpy(bufptr + width - slen, s, (size_t)slen);
                    }

                    bufptr += width;
                }
                break;

            case 'S' : /* unicode String */
                {
                    const DD_UNICODE* uni = NULL ;
                    if ((uni = va_arg(ap, DD_UNICODE *)) == NULL)
                        uni = L"(null)";

                    s = (char*)dd_unicode_to_utf8(uni) ;

                    slen = strlen(s);
                    if (slen > width && prec != width)
                        width = slen;

                    bytes += width;

                    if (bufptr)
                    {
                        if ((bufptr + width) > bufend)
                            width = bufend - bufptr;

                        if (slen > width)
                            slen = width;

                        if (sign == '-')
                        {
                            strncpy(bufptr, s, (size_t)slen);
                            memset(bufptr + slen, ' ', (size_t)(width - slen));
                        }
                        else
                        {
                            memset(bufptr, ' ', (size_t)(width - slen));
                            strncpy(bufptr + width - slen, s, (size_t)slen);
                        }

                        bufptr += width;
                    }

                    __do_destroy(s) ;
                }
                break;

            case 'n' : /* Output number of chars so far */
                *(va_arg(ap, int *)) = bytes;
                break;
            }
        }
        else
        {
            bytes ++;

            if (bufptr && bufptr < bufend)
                *bufptr++ = *format;

            format ++;
        }
    }

    /*
    * Nul-terminate the string and return the number of characters needed.
    */

    *bufptr = '\0';

    return (bytes);
}


// End of File
